/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package bgu.ppm.sim.impl.mas;

import bgu.ppm.sim.api.exec.TickBasedExecutionSystem;
import bgu.ppm.sim.api.mas.Mailer;
import bgu.ppm.sim.api.mas.MultiAgentSystem;
import bgu.ppm.sim.api.mas.TerminationCondition;
import bgu.ppm.sim.api.mas.Agent;
import bgu.ppm.sim.api.mas.AgentFactory;
import bgu.ppm.sim.api.mas.MASModule;
import bgu.ppm.sim.api.mas.Model;
import bgu.sonar.util.reflections.Inject;
import bgu.sonar.util.reflections.Registery;
import bgu.sonar.util.ui.mon.Monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 *
 * @author User
 */
public class MultiAgentSystemImpl<M extends Model> implements MultiAgentSystem<M>, TerminationCondition.TerminationListener {

    private List<Agent> agents;
    @Inject(type = TickBasedExecutionSystem.class)
    private TickBasedExecutionSystem execs;
    @Inject(type = Mailer.class, exceptionIfCannotFindMatch = false)
    private Mailer mailer;
    private M model;
    @Inject(type = TerminationCondition.class)
    private TerminationCondition terminationCondition;
    @Inject(type = AgentFactory.class)
    private AgentFactory agentFactory;
    private Registery registery = new Registery();

    @Override
    public M getGlobalModel() {
        return model;
    }

    @Override
    public Mailer getMailer() {
        return mailer;
    }

    @Override
    public TickBasedExecutionSystem getExecutionSystem() {
        return execs;
    }

    @Override
    public TerminationCondition getTerminationCondition() {
        return terminationCondition;
    }

    @Override
    public void onTerminationNeeded(TerminationCondition condition) {
        execs.stop();
    }

    @Override
    public AgentFactory getAgentFactory() {
        return agentFactory;
    }

    @Override
    public void register(MASModule module) {
        registery.register(module);
    }

    @Override
    public <T extends MASModule> Collection<T> getRegisteredModules(Class<T> type) {
        return registery.lookup(type);
    }

    @Override
    public void process(M model, Monitor m) {
        m.writeln("initializing model");

        //storing and initializing the model
        this.model = model;
        this.model.initialize();


        m.writeln("registering and initializing modules");
        //register me so i can be injected
        registery.register(this);
        //first inject all registered items;
        registery.inject(this);
        final Collection<MASModule> modules = registery.lookup(MASModule.class);
        for (MASModule i : modules) {
            registery.inject(i);
        }

        //now initialize all the modules
        for (MASModule i : modules) {
            i.initialize(this);
        }


        m.writeln("creating agents");
        //now create the agents.
        agents = new ArrayList<>(model.getNumberOfAgents());
        for (int i = 0; i < model.getNumberOfAgents(); i++) {
            final Agent createdAgent = agentFactory.createAgent(i);
            registery.inject(createdAgent);
            agents.add(createdAgent);
        }

        //listen to termination condition
        terminationCondition.getListeners().add(this);

        //insert the agents into the execution system (including the termination condition)
        execs.addAll(agents);
        execs.getListeners().add(terminationCondition);

        m.writeln("executing...");
        //run
        execs.run();

    }

}
